/**
 * trieda urcena na prvotne rozparsovanie DTD suboru na jednotlive elementy,
 * atributy a entity
 */
package dtd.to.xmlschema;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.regex.Pattern;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

/**
 *
 * @author Tomas Plevko <324589@mail.muni.cz>
 * @version 0.1
 */
public class DTDSerializer {

    private TreeMap<String, DTDElement> elementMap = new TreeMap<String, DTDElement>();
    private static final String ELEMENT = "ELEMENT";
    private static final String ATTRIBUTE = "ATTLIST";
    private static final String ENTITY = "ENTITY";

    /**
     * konstruktor triedy DTDSerializer
     */
    public DTDSerializer() {
    }

    /**
     * metoda parseDTD je pouzivana na privolanie XSDCreatora nad mapou
     * obsahujucou vsetky elementy a atributy zo vstupneho DTD.
     *
     * @throws ParserConfigurationException
     */
    public void parseDTD(File output) throws ParserConfigurationException {

        XSDCreator creator = new XSDCreator(elementMap);

        try {
            
            creator.transformDTD();

            creator.printXSD(output);
            
        } catch (ParserConfigurationException e) {
            System.err.println(e);
        } catch (TransformerConfigurationException e) {
            System.err.println(e);
        } catch (TransformerException e) {
            System.err.println(e);
        } catch (IOException e) {
            System.err.println(e);
        }
    }

    /**
     * metoda sluziaca na rozdelenie vstupneho DTD na riadky, podla separatora
     * ">", ktory oznacuje koniec elementu, attlistu, alebo entity DTD suboru.
     *
     * @param in
     * @throws FileNotFoundException
     */
    public void readDTD(File in) throws FileNotFoundException {

        Scanner scanner = null;

        try {

            scanner = new Scanner(new FileReader(in));

            scanner.useDelimiter(">\\s*");

            while (scanner.hasNext()) {
                processLine(scanner.next());
            }

        } catch (Exception e) {
            System.err.println(e);
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }

    }

    /**
     * metoda processLine rozdeluje jednotlive riadky pre obsluzne triedy -
     * podla toho, ci riadok obsahuje entitu, element, alebo attlist.
     *
     * @param line - riadok vstupneho DTD suboru
     */
    public void processLine(String line) {


        String[] separatedLine;

        if (line != null) {
            separatedLine = line.split("\\s");

            if (Pattern.matches("<!" + ATTRIBUTE, separatedLine[0])) {
                handleAttribute(line);
            }

            if (Pattern.matches("<!" + ELEMENT, separatedLine[0])) {
                handleElement(line);
            }

            if (Pattern.matches("<!" + ENTITY, separatedLine[0])) {
                handleEntity(line);
            }
        }

    }

    /**
     * metoda sluziaca na rozparsovanie riadku obsahujuceho element zo vstupneho
     * DTD. Metoda rozlozi riadok na meno elementu - prva cast riadku, a
     * kategoriu elementu - zvysok riadku. Novy element prida do mapy elementov.
     *
     * @param line - riadok obsahujuci element zo vstupneho DTD
     */
    public void handleElement(String line) {

        String[] separatedLine;
        String elementName;
        DTDElement element = new DTDElement();
        int indexOfProperties;
        String properties;

        separatedLine = line.split("\\s+");

        elementName = separatedLine[1];

        element.setName(elementName);

        indexOfProperties = line.indexOf(separatedLine[2]);

        properties = line.substring(indexOfProperties);

        element.setProperties(properties);

        elementMap.put(elementName, element);
    }

    /**
     * metoda handleAttribute ma za ulohu rozparsovat riadok, alebo riadky
     * obsahujuce attlist, ktory obsahuje atributy. Prva cast riadku obsahuje
     * meno elementu, ktoremu atribut patri, dalsia meno atributu, dalsia typ
     * atributu a posledna defaultnu hodnotu. Atribut sa prida do mapy
     * elementov, konkretne elementu, ktoremu atributy patia
     *
     * @param line - riadok obsahujuci cast DTD attlist
     */
    public void handleAttribute(String line) {

        String[] separatedLine;
        String attributeName;

        separatedLine = line.split("\\s+");

        for (int i = 2; i < (separatedLine.length - 1);) {

            DTDAttribute attribute = new DTDAttribute();

            attribute.setElementName(separatedLine[1]);

            attributeName = separatedLine[i];

            attribute.setAttributeName(attributeName);

            if (separatedLine[i + 1].startsWith("(")) {

                String type = separatedLine[++i];

                while (!type.endsWith(")")) {

                    type += separatedLine[++i];
                }

                attribute.setAttributeType(type);
            } else {

                attribute.setAttributeType(separatedLine[++i]);
            }

            if (separatedLine[i + 1].equals("#FIXED")) {

                String defValue = separatedLine[++i] + " " + separatedLine[++i];

                attribute.setDefaultValue(defValue);

            } else {

                attribute.setDefaultValue(separatedLine[++i]);

            }

            DTDElement element = elementMap.get(separatedLine[1]);

            element.insertNewAttribute(attribute, attributeName);
        }
    }

    /**
     * metoda sa nemusi implementovat, entity sa v nasej implementacii nijakym
     * sposobom nespracovavaju
     *
     * @param line
     */
    public void handleEntity(String line) {
    }
    
    /**
     * metoda na ziskanie mapy elementov
     * 
     * @return 
     */
    public TreeMap<String, DTDElement> getElementMap() {
        return elementMap;
    }
}
